package com.imut.lagain.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imut.lagain.entity.MenstrualCycle;
import com.imut.lagain.repository.MenstrualCycleRepository;
import com.imut.lagain.service.MenstrualCycleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 生理期管理服务实现类
 */
@Service
public class MenstrualCycleServiceImpl extends ServiceImpl<MenstrualCycleRepository, MenstrualCycle> implements MenstrualCycleService {
    private static final Logger log = LoggerFactory.getLogger(MenstrualCycleServiceImpl.class);
    
    @Autowired
    private MenstrualCycleRepository menstrualCycleRepository;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    // 默认周期长度
    private static final int DEFAULT_CYCLE_LENGTH = 28;
    // 默认经期长度
    private static final int DEFAULT_PERIOD_LENGTH = 5;
    // 预测数据的最大月数
    private static final int MAX_PREDICTION_MONTHS = 12;
    
    @Override
    @Transactional
    public MenstrualCycle createCycle(MenstrualCycle cycle, Long userId) {
        log.info("创建生理期记录，用户ID: {}", userId);
        
        cycle.setUserId(userId);
        cycle.setIsPredicted(false);
        
        // 如果没有设置结束日期，根据经期长度计算
        if (cycle.getEndDate() == null && cycle.getPeriodLength() != null) {
            cycle.setEndDate(cycle.getStartDate().plusDays(cycle.getPeriodLength() - 1));
        }
        
        // 如果没有设置周期长度，使用默认值
        if (cycle.getCycleLength() == null) {
            cycle.setCycleLength(DEFAULT_CYCLE_LENGTH);
        }
        
        // 如果没有设置经期长度，使用默认值
        if (cycle.getPeriodLength() == null) {
            cycle.setPeriodLength(DEFAULT_PERIOD_LENGTH);
        }
        
        boolean success = save(cycle);
        if (success) {
            // 创建新记录后，刷新预测数据
            refreshPredictions(userId);
            return cycle;
        }
        
        throw new RuntimeException("创建生理期记录失败");
    }
    
    @Override
    @Transactional
    public MenstrualCycle updateCycle(MenstrualCycle cycle, Long userId) {
        log.info("更新生理期记录，ID: {}, 用户ID: {}", cycle.getId(), userId);
        
        MenstrualCycle existing = getById(cycle.getId());
        if (existing == null || !existing.getUserId().equals(userId)) {
            throw new RuntimeException("记录不存在或无权限访问");
        }
        
        // 更新字段
        existing.setStartDate(cycle.getStartDate());
        existing.setEndDate(cycle.getEndDate());
        existing.setCycleLength(cycle.getCycleLength());
        existing.setPeriodLength(cycle.getPeriodLength());
        existing.setFlowLevel(cycle.getFlowLevel());
        existing.setSymptoms(cycle.getSymptoms());
        existing.setNotes(cycle.getNotes());
        
        boolean success = updateById(existing);
        if (success) {
            // 更新记录后，刷新预测数据
            refreshPredictions(userId);
            return existing;
        }
        
        throw new RuntimeException("更新生理期记录失败");
    }
    
    @Override
    @Transactional
    public boolean deleteCycle(Long id, Long userId) {
        log.info("删除生理期记录，ID: {}, 用户ID: {}", id, userId);
        
        MenstrualCycle existing = getById(id);
        if (existing == null || !existing.getUserId().equals(userId)) {
            return false;
        }
        
        boolean success = removeById(id);
        if (success) {
            // 删除记录后，刷新预测数据
            refreshPredictions(userId);
        }
        
        return success;
    }
    
    @Override
    public IPage<MenstrualCycle> getUserCycles(Long userId, Page<MenstrualCycle> page) {
        return menstrualCycleRepository.findByUserId(userId, page);
    }
    
    @Override
    public List<MenstrualCycle> getCyclesByDateRange(Long userId, LocalDate startDate, LocalDate endDate) {
        return menstrualCycleRepository.findByUserIdAndDateRange(userId, startDate, endDate);
    }
    
    @Override
    public Map<String, Object> getCycleStatusByDate(Long userId, LocalDate date) {
        Map<String, Object> status = new HashMap<>();
        
        MenstrualCycle cycle = menstrualCycleRepository.findByUserIdAndDate(userId, date);
        
        if (cycle != null) {
            status.put("inPeriod", true);
            status.put("isPredicted", cycle.getIsPredicted());
            status.put("flowLevel", cycle.getFlowLevel());
            status.put("dayOfPeriod", ChronoUnit.DAYS.between(cycle.getStartDate(), date) + 1);
            
            // 解析症状数据
            if (cycle.getSymptoms() != null) {
                try {
                    Map<String, Object> symptoms = objectMapper.readValue(cycle.getSymptoms(), Map.class);
                    status.put("symptoms", symptoms);
                } catch (JsonProcessingException e) {
                    log.warn("解析症状数据失败: {}", e.getMessage());
                }
            }
        } else {
            status.put("inPeriod", false);
            status.put("isPredicted", false);
            
            // 计算距离下次生理期的天数
            LocalDate nextDate = getNextPredictedDate(userId);
            if (nextDate != null) {
                long daysUntilNext = ChronoUnit.DAYS.between(date, nextDate);
                status.put("daysUntilNext", daysUntilNext);
            }
        }
        
        return status;
    }
    
    @Override
    @Transactional
    public List<MenstrualCycle> predictFutureCycles(Long userId, int months) {
        log.info("预测未来生理期，用户ID: {}, 月数: {}", userId, months);
        
        if (months > MAX_PREDICTION_MONTHS) {
            months = MAX_PREDICTION_MONTHS;
        }
        
        // 获取最近的实际记录
        List<MenstrualCycle> recentCycles = menstrualCycleRepository.findRecentByUserId(userId, 6);
        if (recentCycles.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 计算平均周期长度和经期长度
        Double avgCycleLength = menstrualCycleRepository.getAverageCycleLength(userId, 6);
        Double avgPeriodLength = menstrualCycleRepository.getAveragePeriodLength(userId, 6);
        
        int cycleLength = avgCycleLength != null ? avgCycleLength.intValue() : DEFAULT_CYCLE_LENGTH;
        int periodLength = avgPeriodLength != null ? avgPeriodLength.intValue() : DEFAULT_PERIOD_LENGTH;
        
        // 获取最后一次实际记录
        MenstrualCycle lastCycle = recentCycles.get(0);
        LocalDate lastStartDate = lastCycle.getStartDate();
        
        List<MenstrualCycle> predictions = new ArrayList<>();
        LocalDate currentDate = LocalDate.now();
        LocalDate nextStartDate = lastStartDate.plusDays(cycleLength);
        
        // 如果下次预测日期已经过去，调整到当前日期之后
        while (nextStartDate.isBefore(currentDate)) {
            nextStartDate = nextStartDate.plusDays(cycleLength);
        }
        
        // 生成预测数据
        for (int i = 0; i < months * 2; i++) { // 每月可能有2次周期
            if (nextStartDate.isAfter(currentDate.plusMonths(months))) {
                break;
            }
            
            MenstrualCycle prediction = new MenstrualCycle();
            prediction.setUserId(userId);
            prediction.setStartDate(nextStartDate);
            prediction.setEndDate(nextStartDate.plusDays(periodLength - 1));
            prediction.setCycleLength(cycleLength);
            prediction.setPeriodLength(periodLength);
            prediction.setIsPredicted(true);
            
            predictions.add(prediction);
            nextStartDate = nextStartDate.plusDays(cycleLength);
        }
        
        return predictions;
    }
    
    @Override
    public Map<String, Object> getCycleStatistics(Long userId) {
        Map<String, Object> stats = new HashMap<>();
        
        // 获取最近6次记录进行统计
        List<MenstrualCycle> recentCycles = menstrualCycleRepository.findRecentByUserId(userId, 6);
        
        if (!recentCycles.isEmpty()) {
            // 平均周期长度
            Double avgCycleLength = menstrualCycleRepository.getAverageCycleLength(userId, 6);
            stats.put("averageCycleLength", avgCycleLength != null ? avgCycleLength.intValue() : DEFAULT_CYCLE_LENGTH);
            
            // 平均经期长度
            Double avgPeriodLength = menstrualCycleRepository.getAveragePeriodLength(userId, 6);
            stats.put("averagePeriodLength", avgPeriodLength != null ? avgPeriodLength.intValue() : DEFAULT_PERIOD_LENGTH);
            
            // 最后一次生理期
            MenstrualCycle lastCycle = recentCycles.get(0);
            stats.put("lastPeriodDate", lastCycle.getStartDate());
            
            // 下次预计日期
            LocalDate nextDate = getNextPredictedDate(userId);
            stats.put("nextPredictedDate", nextDate);
            
            // 周期规律性（标准差）
            if (recentCycles.size() >= 3) {
                List<Integer> cycleLengths = recentCycles.stream()
                    .filter(c -> c.getCycleLength() != null)
                    .map(MenstrualCycle::getCycleLength)
                    .collect(Collectors.toList());
                
                if (cycleLengths.size() >= 2) {
                    double variance = calculateVariance(cycleLengths);
                    stats.put("cycleRegularity", variance < 4 ? "规律" : variance < 7 ? "较规律" : "不规律");
                }
            }
        }
        
        return stats;
    }
    
    @Override
    public Map<String, Object> getMonthlyCalendarData(Long userId, int year, int month) {
        LocalDate startDate = LocalDate.of(year, month, 1);
        LocalDate endDate = startDate.plusMonths(1).minusDays(1);
        
        // 获取该月的所有记录（包括预测）
        List<MenstrualCycle> cycles = getCyclesByDateRange(userId, startDate, endDate);
        
        // 获取预测数据
        List<MenstrualCycle> predictions = predictFutureCycles(userId, 2);
        cycles.addAll(predictions.stream()
            .filter(p -> !p.getStartDate().isAfter(endDate) && !p.getEndDate().isBefore(startDate))
            .collect(Collectors.toList()));
        
        Map<String, Object> calendarData = new HashMap<>();
        Map<String, Object> dayData = new HashMap<>();
        
        // 处理每一天的数据
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            Map<String, Object> dayInfo = getCycleStatusByDate(userId, date);
            
            // 添加排卵期信息
            Map<String, Object> ovulation = getOvulationPrediction(userId);
            if (ovulation.containsKey("ovulationDate")) {
                LocalDate ovulationDate = (LocalDate) ovulation.get("ovulationDate");
                LocalDate fertileStart = (LocalDate) ovulation.get("fertileStart");
                LocalDate fertileEnd = (LocalDate) ovulation.get("fertileEnd");
                
                if (date.equals(ovulationDate)) {
                    dayInfo.put("isOvulationDay", true);
                } else if (!date.isBefore(fertileStart) && !date.isAfter(fertileEnd)) {
                    dayInfo.put("isFertileDay", true);
                }
            }
            
            dayData.put(date.toString(), dayInfo);
        }
        
        calendarData.put("year", year);
        calendarData.put("month", month);
        calendarData.put("dayData", dayData);
        calendarData.put("statistics", getCycleStatistics(userId));
        
        return calendarData;
    }
    
    @Override
    @Transactional
    public boolean recordSymptoms(Long userId, LocalDate date, Map<String, Object> symptoms) {
        try {
            // 查找该日期的记录
            MenstrualCycle cycle = menstrualCycleRepository.findByUserIdAndDate(userId, date);
            
            if (cycle != null) {
                // 更新现有记录的症状
                String symptomsJson = objectMapper.writeValueAsString(symptoms);
                cycle.setSymptoms(symptomsJson);
                return updateById(cycle);
            } else {
                // 创建新的症状记录
                MenstrualCycle newCycle = new MenstrualCycle();
                newCycle.setUserId(userId);
                newCycle.setStartDate(date);
                newCycle.setSymptoms(objectMapper.writeValueAsString(symptoms));
                newCycle.setIsPredicted(false);
                return save(newCycle);
            }
        } catch (JsonProcessingException e) {
            log.error("记录症状失败: {}", e.getMessage());
            return false;
        }
    }
    
    @Override
    public LocalDate getNextPredictedDate(Long userId) {
        MenstrualCycle lastCycle = menstrualCycleRepository.findLastActualCycleByUserId(userId);
        if (lastCycle == null) {
            return null;
        }
        
        Double avgCycleLength = menstrualCycleRepository.getAverageCycleLength(userId, 6);
        int cycleLength = avgCycleLength != null ? avgCycleLength.intValue() : DEFAULT_CYCLE_LENGTH;
        
        LocalDate nextDate = lastCycle.getStartDate().plusDays(cycleLength);
        LocalDate currentDate = LocalDate.now();
        
        // 如果预测日期已经过去，调整到下一个周期
        while (nextDate.isBefore(currentDate)) {
            nextDate = nextDate.plusDays(cycleLength);
        }
        
        return nextDate;
    }
    
    @Override
    public Map<String, Object> getOvulationPrediction(Long userId) {
        Map<String, Object> ovulation = new HashMap<>();
        
        LocalDate nextPeriodDate = getNextPredictedDate(userId);
        if (nextPeriodDate == null) {
            return ovulation;
        }
        
        // 排卵日通常在下次月经前14天
        LocalDate ovulationDate = nextPeriodDate.minusDays(14);
        LocalDate fertileStart = ovulationDate.minusDays(5); // 受孕期开始
        LocalDate fertileEnd = ovulationDate.plusDays(1);   // 受孕期结束
        
        ovulation.put("ovulationDate", ovulationDate);
        ovulation.put("fertileStart", fertileStart);
        ovulation.put("fertileEnd", fertileEnd);
        ovulation.put("nextPeriodDate", nextPeriodDate);
        
        return ovulation;
    }
    
    @Override
    @Transactional
    public boolean updateCycleLength(Long userId, Integer cycleLength) {
        // 更新最近的记录的周期长度
        MenstrualCycle lastCycle = menstrualCycleRepository.findLastActualCycleByUserId(userId);
        if (lastCycle != null) {
            lastCycle.setCycleLength(cycleLength);
            boolean success = updateById(lastCycle);
            if (success) {
                refreshPredictions(userId);
            }
            return success;
        }
        return false;
    }
    
    @Override
    @Transactional
    public boolean refreshPredictions(Long userId) {
        try {
            // 删除现有的预测数据
            menstrualCycleRepository.deletePredictedDataByUserId(userId);
            
            // 生成新的预测数据
            List<MenstrualCycle> predictions = predictFutureCycles(userId, 6);
            
            // 保存预测数据
            for (MenstrualCycle prediction : predictions) {
                save(prediction);
            }
            
            log.info("刷新预测数据成功，用户ID: {}, 生成 {} 条预测记录", userId, predictions.size());
            return true;
        } catch (Exception e) {
            log.error("刷新预测数据失败，用户ID: {}", userId, e);
            return false;
        }
    }
    
    /**
     * 计算方差
     */
    private double calculateVariance(List<Integer> values) {
        if (values.size() < 2) {
            return 0;
        }
        
        double mean = values.stream().mapToInt(Integer::intValue).average().orElse(0);
        double variance = values.stream()
            .mapToDouble(v -> Math.pow(v - mean, 2))
            .average()
            .orElse(0);
        
        return Math.sqrt(variance);
    }
}